(() => {
  // -----------------------1. 定义对象结构-----------------------
  interface LabelValue {
    label: string
  }
  //【接口的灵活性】该函数定义了接口类型，表示传参只要含有接口对象里的属性即可。允许接受其它类型
  function printLabel(labelObj: LabelValue) {
    console.log(labelObj.label);
  }
  let myObj = { size: 10, label: 'my label sie is 10' }
  printLabel(myObj)

  /**
   * function printLabel1(label: string){
   * console.log(label);
   * }
   * let myObj1 = {size: 10, label:'my label sie is 10'}
   * printLabel1(myObj1) // 报错，类型“{ size: number; label: string; }”的参数不能赋给类型“string”的参数。
   * 
   */

  // -----------------------2. 可选属性 ？-----------------------
  interface Person {
    b?: string,
    a: string
  }
  // 接口不要求属性位置对应，只要属性存在就行
  const person: Person = {
    a: '213',
    b: '123'
  }
  // 可选属性可以不赋值
  const person2: Person = {
    a: '213'
  }

  // -----------------------3. 只读属性-----------------------
  interface Point {
    readonly x: number,
    readonly y: number
  }
  let p1: Point = {
    x: 3,
    y: 9
  }
  // p1.x = 4 // 报错，不能修改只读属性
  /**
   * readonly VS const
   * 1. readonly 修饰的属性只能在声明时或构造函数里初始化，不能在运行时修改。
   * 2. const 修饰的属性在声明时必须初始化，不能再次赋值。
   */

  // -----------------------4. 额外的属性检查-----------------------
  interface Person {
    b?: string | undefined,
    a: string,
    // [propName: 类型] 类型必须包含Person指定类型的所有类型，否则会报错
    [propName: string]: boolean | string | undefined;
  }
  const person3: Person = {
    a: '213',
    b: '123',
    age: true
  }

  // -----------------------5. 函数类型-----------------------
  interface SearchFunc {
    (source: string, subString: string): boolean;
  }
  let mySearch: SearchFunc
  mySearch = function (source: string, subString: string) {
    // String.search()方法：找到返回返回它首次出现的索引，否则返回-1
    let result = source.search(subString)
    return result > -1
  }
  console.log(mySearch('hello world', 'world')) // true

  // 函数的参数名不需要与接口里定义的名字匹配
  const mySearch2: SearchFunc = function (s: string, subS: string) {
    let result = s.search(subS)
    return result > -1
  }
  console.log(mySearch2('hello world', 'world')) // true

  /**
   * void 直接限制 VS 接口限制
   * 直接限制 返回类型为 void 时，TypeScript 会严格执行，不允许返回任何类型的值。
   * 接口限制 返回类型为 void 时，TypeScript 允许返回值。
   */
  function fun222(): void {
    // return '123' 报错
  }
  interface IFun1 {
    (): void;
  }
  let fun111: IFun1 = function () {
    return '22'
  }
  console.log(fun111());  //不仅return有返回值不报错，而且能获取返回值

  // -----------------------6. 可索引的类型-----------------------
  interface StringArray {
    [index: number]: string;
  }

  let myArray: StringArray
  myArray = ['hello', 'world']
  console.log(myArray[0]) // hello

  // -----------------------7. 重名合并-----------------------
  interface A { name: string }
  interface A { age: number }
  let a1: A = { name: 'zhangsan', age: 20 }
  console.log(a1.name) // zhangsan

  // 重名合并后，属性会合并到一起
  // interface A { name: string }
  // interface A { name: number } // 报错，不能合并不同类型属性

  // -----------------------8. 接口继承-----------------------
  interface A1 {
    name: string,
    sex: string
  }

  interface B extends A1 {
    age: number,
    // sex: number 报错，不能重写父接口的属性
  }

  // 继承过来的属性必须全部实现，否则会报错
  let obj: B = {
    name: 'zhangsan',
    age: 20,
    sex: '男'
  }

  //一个接口可以继承多个接口，创建出多个接口的合成接口
  interface Shape {
    color: string
  }
  interface PenStroke {
    penWidth: number,
  }
  // 同时定义的多个类型接口有重名属性不同类型时会报错
  interface Square extends Shape, PenStroke {
    sideLength: number
  }
  let square: Square = {
    color: 'blue',
    penWidth: 3,
    sideLength: 10
  }

  // -----------------------9. 描述混合类型-----------------------
  /**
   * 描述混合类型
   * 如：一个对象可以同时做为函数和对象使用，并带有额外的属性
   */

  interface Counter {
    (start: number): string   //构造签名，表明该接口是一个函数
    interval: number  // 属性:(间隔时间)
    reset(): void  // 方法:(重置计数器)
  }
  function getCounter(): Counter {
    // let counter = <Counter>function(start: number){} 否则会报错
    let counter = function (start: number) {
      return 'Current count:' + start
    } as Counter
    counter.interval = 123
    counter.reset = function () {
      counter.interval = 0
    }
    // 函数返回参数设置为Counter,意味着要返回一个Counter混合对象
    return counter
  }
  let c = getCounter()
  console.log(c(10));
  c.interval = 5.0
  console.log(c(10));

  // -----------------------10. 接口继承类-----------------------
})()

